package com.libgdxbar.pvzlikegame.asset;

import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.ObjectMap;
import org.libgdxbar.projects.interfaces.asset.AssetBatchCallback;
import org.libgdxbar.projects.interfaces.asset.AssetCallback;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;


public class AssetsLoader implements Disposable {

    private final ObjectMap<String, ObjectMap<Class<?>, Object>> assetsMap = new ObjectMap<>();
    private final AssetsControl control;
    private final Class<?> tagClass;

    AssetsLoader(AssetsControl control, Class<?> tagClass) {
        this.control = control;
        this.tagClass = tagClass;
    }

    /**
     * 同步获取资源
     *
     * @param fileName 资源名
     * @param type     资源类型
     * @return 资源
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String fileName, Class<T> type) {
        if (assetsMap.containsKey(fileName)) {
            ObjectMap<Class<?>, Object> assets = assetsMap.get(fileName);
            if (assets.containsKey(type)) {
                return (T) assets.get(type);
            }
        }
        T asset = control.get(fileName, type);
        setAsset(fileName, type, asset);
        return asset;
    }

    /**
     * 获取所有加载资源
     *
     * @return 资源
     */
    public ObjectMap<String, ObjectMap<Class<?>, Object>> getAssets() {
        return assetsMap;
    }

    /**
     * 判断资源是否已加载
     *
     * @param fileName 资源名
     * @return 是否已加载
     */
    public boolean isLoaded(String fileName) {
        return assetsMap.containsKey(fileName);
    }

    /**
     * 异步获取资源
     *
     * @param fileName 资源名
     * @param type     资源类型
     * @param callback 回调
     */
    public <T> AssetsLoader get(String fileName, Class<T> type, AssetCallback<T> callback) {
        control.get(fileName, type, asset -> {
            setAsset(fileName, type, asset);
            if (Objects.nonNull(callback)) {
                callback.finish(asset);
            }
        });
        return this;
    }

    /**
     * 异步批量获取资源
     *
     * @param batch    批量获取资源
     * @param callback 回调 (参数为最后一个资源)
     */
    public AssetsLoader get(AssetBatch batch, AssetBatchCallback callback) {
        Array<AssetExecution> executions = batch.getExecutions();

        if (Objects.isNull(executions) || executions.isEmpty()) {
            if (Objects.nonNull(callback)) {
                callback.finish();
            }
            return this;
        }

        // 过滤已加载的资源
        Array<AssetExecution> executionQueue = new Array<>();
        for (AssetExecution execution : executions) {
            if (!control.isLoaded(execution.getFileName())) {
                if (!executionQueue.contains(execution, false)) {
                    executionQueue.add(execution);
                }
            } else if (!isLoaded(execution.getFileName())) {
                // 资源管控已经加载了，直接同步获取资源
                get(execution.getFileName(), execution.getType());
            }
        }

        // 没有需要加载的资源，直接回调
        if (executionQueue.isEmpty()) {
            if (Objects.nonNull(callback)) {
                callback.finish();
            }
            return this;
        }
        // 需要加载的资源数量
        int total = executionQueue.size;
        // 已经加载的资源数量
        AtomicInteger progress = new AtomicInteger();

        for (AssetExecution execution : executionQueue) {
            get(execution.getFileName(), execution.getType(), asset -> {
                int p = progress.getAndIncrement() + 1;
                if (Objects.nonNull(callback)) {
                    callback.process(p, total);
                    if (p == total) {
                        callback.finish();
                    }
                }
            });
        }
        return this;
    }

    /**
     * 卸载资源
     *
     * @param fileName 资源名
     * @return 是否卸载成功
     */
    public boolean unload(String fileName) {
        if (assetsMap.containsKey(fileName)) {
            control.unload(this, fileName);
            assetsMap.remove(fileName);
            return true;
        }
        return false;
    }

    /**
     * 卸载所有资源
     */
    public void unloadAll() {
        for (ObjectMap.Entry<String, ObjectMap<Class<?>, Object>> asset : assetsMap) {
            control.unload(this, asset.key);
        }
        assetsMap.clear();
    }

    /**
     * 设置资源
     *
     * @param fileName 资源名
     * @param type     资源类型
     * @param asset    资源
     */
    private <T> void setAsset(String fileName, Class<T> type, T asset) {
        if (!assetsMap.containsKey(fileName)) {
            ObjectMap<Class<?>, Object> newAssets = new ObjectMap<>();
            newAssets.put(type, asset);
            assetsMap.put(fileName, newAssets);
        } else {
            ObjectMap<Class<?>, Object> assets = assetsMap.get(fileName);
            if (!assets.containsKey(type)) {
                assets.put(type, asset);
            }
        }
    }

    @Override
    public void dispose() {
        unloadAll();
        control.unload(tagClass);
    }
}
